在上一篇I7565H1/H2 SDK的檔案架構中有一個現象,lib資料夾中動態庫(.so)旁邊都有一個箭頭指向該檔案,靜態庫(.a)卻沒有,這個現象就和動態庫的版本號有關。
// I7565H1/H2 SDK 的 lib 資料夾部份內容
├── lib
│ ├── libI7565H1H2_64.a
│ ├── libI7565H1H2_64.so -> ../lib/libI7565H1H2_64.so.1
│ ├── libI7565H1H2_64.so.1 -> ../lib/libI7565H1H2_64.so.1.0
│ ├── libI7565H1H2_64.so.1.0
│ ├── libI7565H1H2.a
│ ├── libI7565H1H2_arm.a
│ ├── libI7565H1H2_arm.so -> ../lib/libI7565H1H2_arm.so.1
│ ├── libI7565H1H2_arm.so.1 -> ../lib/libI7565H1H2_arm.so.1.0
│ ├── libI7565H1H2_arm.so.1.0
│ ├── libI7565H1H2.so -> ../lib/libI7565H1H2.so.1
什麼是版本號?
一個箭頭指向動態庫的行為是什麼?
為什麼靜態庫並沒有版本號呢?
以上問題都會在接下來的文章中解答。
庫更新後,發布的版本號碼可以分為三類:
以常用的程式庫OpenCV為例,都遵循著[主版本號.次要版本號.補丁]格式來發布程式庫版本。
在Linux中,這個行為就是軟鍊結(Wiki),動態庫的版本實現機制就是透過軟鍊結來實現的。
// 以這個檔案架構為例
├── libmysqrt.so -> libmysqrt.so.1
├── libmysqrt.so.1 -> libmysqrt.so.1.1.0
└── libmysqrt.so.1.0.0
比如說當程式想要鍊結 mysqrt 這個動態庫,那鍊結器會到指定的目錄去搜尋 mysqrt 這個庫的 SONAME ,也就是 libmysqrt.so 這個檔案,而 libmysqrt.so 指向 libmysqrt.so.1 , libmysqrt.so.1 又指向 libmysqrt.so.1.0.0 因此最後鍊結到的動態庫就是 libmysqrt.so.1.0.0 。
這樣就可以在不更改主程式的鍊結方式或程式碼的狀態下更新函式庫了!因為鍊結器只知道要鍊結到函式庫的 SONAME ,所以只要在更新動態庫的時候一起更改軟鍊結鍊結到的檔案,就可以實現動態庫最方便的特點之一 修改/升級庫程式後,不需要重新編譯/鏈接整個應用程式。
如果對上述的說明不是很理解也沒關係,明天就會開始實際操作如何更新動態庫啦!
因為靜態庫的特性是在編譯時就和其他檔案一起鍊結成執行檔,而動態庫是在運行時才去庫存放的資料夾中尋找所需的檔案,因此就可以實現一個非常方便的功能--動態庫更新時,使用到該動態庫的程式不需要重新編譯。
因為一個大型專案要重新編譯會耗費非常多的時間,而且專案可以和庫分別獨立開發,只要將新版本動態庫安裝到指定目錄或系統目錄就行了,專案可以立刻享受新版本的功能。
答案是不用,因為動態庫有版本號這個機制,讓專案在使用時能夠永遠指向最新版本。
但是能夠不重新編譯整個專案有一個重要的前提,就是ABI(Application Binary Interface)二進制檔接口必須要相同,因此在發布之前需要 ABI Compliance Checker來檢查ABI是否一致。
會改變ABI導致向後不兼容的行為通常有以下幾點:
之後會有範例來說明 ABI 與 如何使用 ABI Checker。
書籍:程序员的自我修养—链接、装载与库
Dynamic linking best practices
Creating Dynamic Libraries
ABI Policy and Guidelines